<!DOCTYPE html>
<html>
<head>
    <title>Vizit</title>
    <link rel="stylesheet" href="css/style.css">
    <script src="node_modules/jquery/dist/jquery.js"></script>
    <script src="node_modules/@svgdotjs/svg.js/dist/svg.js"></script>
    <script src="node_modules/@svgdotjs/svg.draggable.js/dist/svg.draggable.js"></script>
    <script src="scripts/snap.svg.js"></script>
    <script src="http://svg.dabbles.info/animate-object-path.js"></script>
    <!--    <script src="//cdnjs.cloudflare.com/ajax/libs/velocity/2.0.6/velocity.min.js"></script>-->

</head>
<body>

<script>
    // 定义svg对象, 在body里面添加一个svg
    let svg = SVG().addTo('body').attr({width: 800, height: 800, id: 'svg'});
    var snap = Snap("#svg");
    // marker: arrow head
    let marker = svg.marker(13, 13, function (add) {
        add.path('M2,2 L2,11 L10,6 L2,2').fill('blue').stroke({width: 1, color: 'blue'})
    }).attr({"refX": "2", "refY": "6", "orient": "auto"});
    // Set the global configs to synchronous
    $.ajaxSetup({
        async: false
    });
    let version = Date.now();

    function createNode(container, data) {
        // 用一个group把节点边框和文本放在一起，这样可以利用group的transform来定义
        // 整个group里面所有节点的坐标位置
        let translateX = data.group.transform.translateX;
        let translateY = data.group.transform.translateY;
        // let label = container.text(data.label.text);
        let label = container.text('');

        let width = data.group.width == null ? (data.label.text.length * 8 + 10) : parseInt(data.group.width);
        let height = data.group.height == null ? 28 : parseInt(data.group.height);
        let group = container.group()
            .transform({translateX: translateX, translateY: translateY})
            .addClass('leaf')
            .id(data.group.id)
            .data('leftTopX', translateX)
            .data('leftTopY', translateY)
            .data('width', width)
            .data('height', height)
        ;
        // TODO style 的信息暂时hardcode, 后面会用程序控制动态变化的效果。
        let style = {fill: 'none', stroke: 'blue'};
        let attributes = {...style, ...data.shape.attributes};
        // 动态的调用函数，可以支持绘制各种图形，比如:rect, ellipse
        let rect = container[data.shape.type](width, height).attr(attributes);
        group.add(rect);

        label.attr(data.label.attributes);
        group.add(label);
        return group.draggable(); // 支持拖拽
    }

    function buildPathId(start, end) {
        return `${start}_${end}`;
    }

    function buildPath(start, endList, i, path) {
        let startNode = svg.findOne(`#${start}`);
        let endNode = svg.findOne(`#${endList[i]}`);
        let startX = startNode.data('leftTopX') + startNode.data('width') + path.startOffsetX;
        let startY = startNode.data('leftTopY') + startNode.data('height') + path.startOffsetY;
        let endX = endNode.data('leftTopX') + path.endOffsetX;
        let endY = endNode.data('leftTopY') + path.endOffsetY;
        let data = `M ${startX}, ${startY} ${endX}, ${endY}`;
        if (path.curve != null) {
            data = `M ${startX}, ${startY} ${path.curve} ${endX}, ${endY}`;
        }
        return {startX, startY, endX, endY, data};
    }

    function animate(container, pathId, loop, callback) {
        let points = svg.findOne(`#${pathId}`).data('points');
        container.circle(20).attr({cx: points[0], cy: points[1]}).fill('green')
            .id('circle-' + pathId)
            .animate(2500, 100, 'now')
            .move(points[2] - 10, points[3] - 10)
            .loop(Number.MAX_SAFE_INTEGER, false, Math.random() * 2000)
        // .loop(1, false, 200)
        ;
    }

    function createPath(container, path, marker) {
        let start = path.start;
        let endList = path.end;
        for (let i = 0; i < endList.length; i++) {
            let {startX, startY, endX, endY, data} = buildPath(start, endList, i, path);
            let id = buildPathId(start, endList[i]);
            let pathId = `${start}_${endList[i]}`;
            let group = container.group().id(`data-flow-${pathId}`).addClass('leaf');
            group.path(data).id(id)
                .fill('none')
                .stroke({width: 1, color: 'blue'})
                .marker('end', marker)
                .data('points', [startX, startY, endX, endY])
                .data('d', data);
            animate(group, pathId);
        }
    }

    // a matrix is a group of nodes replicated from data
    function createMatrix(container, data) {
        let group = container.group();
        let matrix = data.shape.matrix;
        if (matrix != null) {
            for (let i = 0; i < matrix.row.count; i++) {
                let row = group.group().id(data.group.id.replace("{row}", i).replace("{column}", 'x'));
                for (let j = 0; j < matrix.column.count; j++) {
                    let nodeReplica = JSON.parse(JSON.stringify(data));
                    nodeReplica.group.transform.translateX += nodeReplica.shape.matrix.column.offset * j;
                    nodeReplica.group.transform.translateY += nodeReplica.shape.matrix.row.offset * i;
                    nodeReplica.label.text = nodeReplica.label.text.replace("{}", j);
                    nodeReplica.group.id = nodeReplica.group.id.replace("{row}", i).replace("{column}", j);
                    createNode(row, nodeReplica);
                }
            }
        }
    }

    SVG.on(document, 'DOMContentLoaded', function () {
        $.getJSON("data/meet/producer.json?" + version, function (node) {
            createMatrix(svg, node);
        });
        $.getJSON("data/meet/kafka.json?" + version, function (node) {
            createMatrix(svg, node);
        });
        $.getJSON("data/meet/consumer.json?" + version, function (node) {
            createMatrix(svg, node);
        });

        function createP2P() {
            let p2p = svg.group().id('path-p2p');
            for (let i = 0; i < 5; i++) {
                for (let j = 0; j < 5; j++) {
                    let path = {
                        "start": `producer-0-${i}`,
                        "end": [
                            `consumer-0-${j}`
                        ],
                        "startOffsetX": -30,
                        "startOffsetY": 20,
                        "endOffsetX": 40,
                        "endOffsetY": -10
                    }
                    createPath(p2p, path, marker);
                }
            }
        }

        function createKafka() {
            let kafka = svg.group().id('path-kafka');
            for (let i = 0; i < 5; i++) {
                for (let j = 0; j < 3; j++) {
                    let path = {
                        "start": `producer-0-${i}`,
                        "end": [
                            `cluster-0-${j}`
                        ],
                        "startOffsetX": -30,
                        "startOffsetY": 20,
                        "endOffsetX": 40,
                        "endOffsetY": -10
                    };
                    createPath(kafka, path, marker);
                }
            }
            for (let i = 0; i < 3; i++) {
                for (let j = 0; j < 5; j++) {
                    let path = {
                        "start": `cluster-0-${i}`,
                        "end": [
                            `consumer-0-${j}`
                        ],
                        "startOffsetX": -30,
                        "startOffsetY": 10,
                        "endOffsetX": 40,
                        "endOffsetY": -10
                    };
                    createPath(kafka, path, marker);
                }
            }
        }

        function show_data_flow(froms, tos) {
            for (let i = 0; i < froms.length; i++) {
                for (let j = 0; j < tos.length; j++) {
                    let from = froms[i];
                    let to = tos[j];
                    snap.select(`#${from}`).attr({display: ''});
                    snap.select(`#${to}`).attr({display: ''});
                    snap.select(`#data-flow-${from}_${to}`).attr({display: ''});
                }
            }
        }

        function hideLeaves() {
            snap.selectAll('.leaf').attr({display: 'none'});
        }
        let steps = [
            [showP2P, hideLeaves],
            [show_data_flow.bind(null, ['producer-0-2'], [`consumer-0-2`])],
            [show_data_flow.bind(null, ['producer-0-1'], [`consumer-0-2`])],
            [show_data_flow.bind(null, ['producer-0-3'], [`consumer-0-2`, `consumer-0-3`, `consumer-0-4`])]
        ]
        let stepIndex = 0;

        $("#nextP2P").click(function () {
            let step = steps[stepIndex++];
            for (let i = 0; i < step.length; i++) {
                step[i]();
            }
        });

        let kafka_steps = [
            [showKafka, hideLeaves],
            [show_data_flow.bind(null, ['producer-0-2'], [`cluster-0-1`])],
            [show_data_flow.bind(null, ['cluster-0-1'], [`consumer-0-2`])],
            [show_data_flow.bind(null, ['producer-0-1'], [`cluster-0-1`])],
            [show_data_flow.bind(null, ['cluster-0-1'], [`consumer-0-1`, `consumer-0-3`, `consumer-0-4`])]
        ]
        let kafka_stepIndex = 0;

        $("#nextKafka").click(function () {
            let step = kafka_steps[kafka_stepIndex++];
            for (let i = 0; i < step.length; i++) {
                step[i]();
            }
        });

        $("#hide_all").click(function () {
            snap.select('#path-p2p').attr({display: 'none'});
            snap.select('#path-kafka').attr({display: 'none'});
            snap.select('#producer-0-x').attr({display: 'none'});
            snap.select('#cluster-0-x').attr({display: 'none'});
            snap.select('#consumer-0-x').attr({display: 'none'});
            stepIndex = 0;
        });

        function showP2P() {
            console.log("showP2P...")
            snap.selectAll('.leaf').attr({display: ''});
            snap.select('#path-kafka').attr({display: 'none'});
            snap.select('#path-p2p').attr({display: ''});
            snap.select('#producer-0-x').attr({display: ''});
            snap.select('#cluster-0-x').attr({display: 'none'});
            snap.select('#consumer-0-x').attr({display: ''});
        }

        $("#show_p2p").click(function () {
            showP2P();
        });

        function showKafka() {
            snap.selectAll('.leaf').attr({display: ''});
            snap.select('#path-p2p').attr({display: 'none'});
            snap.select('#path-kafka').attr({display: ''});
            snap.select('#producer-0-x').attr({display: ''});
            snap.select('#cluster-0-x').attr({display: ''});
            snap.select('#consumer-0-x').attr({display: ''});
        }

        $("#show_kafka").click(function () {
            showKafka();
        });
        setTimeout(function () {
            createP2P();
            createKafka();
            snap.select(`#producer-0-0 text`).attr({text: `主页`, x: 26, y: 28});
            snap.select(`#producer-0-1 text`).attr({text: `私信`, x: 26, y: 28});
            snap.select(`#producer-0-2 text`).attr({text: `文章`, x: 26, y: 28});
            snap.select(`#producer-0-3 text`).attr({text: `评论`, x: 26, y: 28});
            snap.select(`#producer-0-4 text`).attr({text: `视频`, x: 26, y: 28});

            snap.select(`#cluster-0-0 text`).attr({text: `日志`, x: 20, y: 20});
            snap.select(`#cluster-0-1 text`).attr({text: `性能`, x: 20, y: 20});
            snap.select(`#cluster-0-2 text`).attr({text: `点赞`, x: 20, y: 20});

            snap.select(`#consumer-0-0 text`).attr({text: `推荐`, x: 26, y: 28});
            snap.select(`#consumer-0-1 text`).attr({text: `分析`, x: 26, y: 28});
            snap.select(`#consumer-0-2 text`).attr({text: `监控`, x: 26, y: 28});
            snap.select(`#consumer-0-3 text`).attr({text: `显示`, x: 26, y: 28});
            snap.select(`#consumer-0-4 text`).attr({text: `AI`, x: 26, y: 28});

            // var c1 = snap.circle(310,325,15).attr({ stroke: 'blue', 'strokeWidth': 1, fill: 'none' });
            // var c2 = snap.circle(380,325,15).attr({ stroke: 'blue', 'strokeWidth': 1, fill: 'none' });
            //
            // // snap.group(c1, c2).attr({ mask: snap.select('#cluster-0-1 rect') });
            // snap.select('#cluster-0-1 rect').attr({ mask: snap.group(c1, c2) });

        }, 500);
    });
</script>
<input id="nextP2P" type="button" class="button" value="Next P2P" style="float: right;">
<input id="nextKafka" type="button" class="button" value="Next Kafka" style="float: right;">
<input id="show_p2p" type="button" class="button" value="Show P2P" style="float: right;">
<input id="show_kafka" type="button" class="button" value="Show Kafka" style="float: right;">
<input id="hide_all" type="button" class="button" value="Hide all" style="float: right;">
</body>
</html>